package com.xohm.diagonstic.tool;

import com.xohm.base.logging.XohmLogger;

import java.io.*;
import java.net.*;
import java.util.Date;

public class Worker implements HttpConstants, Runnable
{
	private WebServer webServer = null;
	
	public final static int BUF_SIZE = 2048;
	public final static byte[] EOL = { (byte) '\r', (byte) '\n' };

	public byte[] buf;  // buffer to use for requests
	private Socket socket;   // Socket to client we're handling
	public static java.util.Hashtable<String, String> map = new java.util.Hashtable<String, String>();  // Mapping of file extensions to content-types
	static
	{
		fillMap();
	}

	public Worker(WebServer webServer)
	{
		this.webServer = webServer;
		buf = new byte[2048];
		socket = null;
	}

	public synchronized void setSocket(Socket socket)
	{
		this.socket = socket;
		notify();
	}

	public synchronized void run()
	{
		while (true)
		{
			if (socket == null)
			{
				/* nothing to do */
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					continue;
				}
			}
			
			try
			{
				handleClient();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
	}

	private void handleClient() throws IOException
	{
		InputStream is = new BufferedInputStream(socket.getInputStream());
		PrintStream ps = new PrintStream(socket.getOutputStream());
		
		socket.setSoTimeout(webServer.timeout);

		/* zero out the buffer from last time */
		for (int i = 0; i<buf.length;i++)
		buf[i] = 0;

		try
		{
			// Only support HTTP GET/HEAD.
			int nread = 0;
			int r = 0;

			boolean done = false;
			while (!done)
			{
				r = is.read(buf, nread, BUF_SIZE - nread);
				if (r == -1)
				{
					return;  // EOF
				}
				
				int start=nread;
				nread += r;
				for (int i=start; i<nread; i++)
				{
					if (buf[i] == (byte)'\n' || buf[i] == (byte)'\r')
					{
						done = true;
						break;
					}
				}
			}

			int index;
			if (buf[0] == (byte)'G' &&
				 buf[1] == (byte)'E' &&
				 buf[2] == (byte)'T' &&
				 buf[3] == (byte)' ')
			{
				index = 4;
			}
			else
			{
				// Only support GET's
				XohmLogger.warningPrintln("Only GET's are supported: " + socket.getInetAddress().getHostAddress() + " : " + new String(buf, 0, buf.length), null, null);
				ps.print("HTTP/1.0 " + HTTP_BAD_METHOD + " unsupported method type: ");
				ps.write(buf, 0, 5);
				ps.write(EOL);
				ps.flush();
				socket.close();
				return;
			}

			int i = 0;
			for (i = index+1; i < nread; i++) {
				if (buf[i] == (byte)' ') {
					break;
				}
			}
			String fname = (new String(buf, index+1, i-(index+1))).replace('/', File.separatorChar);
			if (fname.startsWith(File.separator)) {
				fname = fname.substring(1);
			}
			fname = "webserver/"+fname;
			File targ = new File(webServer.root, fname);
			if (targ.isDirectory()) {
				File ind = new File(targ, "index.html");
				if (ind.exists()) {
					targ = ind;
				}
			}
			boolean OK = printHeaders(targ, ps);
			if (OK) {       
				sendFile(targ, ps);
			} else {
				send404(targ, ps);
			}
		}
		finally
		{
			socket.close();
			socket = null;
			webServer.threads.addElement(this);
		}
	}

	private boolean printHeaders(File targ, PrintStream ps) throws IOException
	{
		boolean ok = false;
		int rCode = 0;
		if (!targ.exists())
		{
			rCode = HTTP_NOT_FOUND;
			ps.print("HTTP/1.0 " + HTTP_NOT_FOUND + " not found");
			ps.write(EOL);
			ok = false;
		}
		else
		{
			rCode = HTTP_OK;
			ps.print("HTTP/1.0 " + HTTP_OK + " OK");
			ps.write(EOL);
			ok = true;
		}
		
		XohmLogger.debugPrintln("From " + socket.getInetAddress().getHostAddress() + ": GET " + targ.getAbsolutePath() + "-->" + rCode);
		ps.print("Server: Xohm Diagnostics");
		ps.write(EOL);
		ps.print("Date: " + (new Date()));
		ps.write(EOL);
		if (ok)
		{
			if (!targ.isDirectory())
			{
				ps.print("Content-length: " + targ.length());
				ps.write(EOL);
				ps.print("Last Modified: " + (new Date(targ.lastModified())));
				ps.write(EOL);
				String name = targ.getName();
				int ind = name.lastIndexOf('.');
				String ct = null;
				if (ind > 0)
				{
					ct = (String) map.get(name.substring(ind));
				}
				if (ct == null)
				{
					ct = "unknown/unknown";
				}
				ps.print("Content-type: " + ct);
				ps.write(EOL);
			}
			else
			{
				ps.print("Content-type: text/html");
				ps.write(EOL);
			}
		}
		return ok;
	}

	private void send404(File targ, PrintStream ps) throws IOException
	{
		ps.write(EOL);
		ps.write(EOL);
		ps.println("Not Found\n\n" + "The requested resource was not found.\n");
	}

	private void sendFile(File targ, PrintStream ps) throws IOException
	{
		/***************************************************************************
		 * 
		 * TODO:  Need to put dignostics information here & remove the files
		 * 
		 * Page 1 -  Shows the following information:
		 * o  WiMAX PHY (Manufacturer & driver version)
		 * o  signal quality (RSSI and CINR) at the end of the current polling interval
		 * o  average signal quality (RSSI and CINR) quality during the current polling interval (one sample per second)
		 * o  signal quality (RSSI and CINR) maximum and minimum during the current polling interval
		 * o  current WiMAX channel number or frequency
		 * o  number of received and transmitted WiMAX frames during the current polling interval
		 * o  total received frames with error during the current polling interval
		 * o  received frames error rate during the current polling interval (not available in Common API).
		 * o  current PRL version
		 * o  Links to Connection and History logs
		 * 
		 * Page 2 - Connection Log contains the following information
		 * o  Date/Time stamp
		 * o  Operation Type (Connect/Disconnect)
		 * o  
		 * 
		 * Page 3 - History Log contains the following information
		 * o  Date/Time stamp
		 * o  Operation Type (Connect/Disconnect)
		 * o  
		 * 
		 ***************************************************************************/
		InputStream is = null;
		ps.write(EOL);
		if (targ.isDirectory())
		{
			/*
			 * here, we take advantage of the fact that FileURLConnection will
			 * parse a directory listing into HTML for us.
			 */
			File ind = new File(targ, "index.html");
			if (ind.exists())
			{
				is = new FileInputStream(ind);
			}
			else
			{
				URL u = new URL("file", "", targ.getAbsolutePath());
				is = u.openStream();
			}
		}
		else
		{
			is = new FileInputStream(targ.getAbsolutePath());
		}
		
		String pageName = targ.getName();
		if (targ.getName().indexOf("/") > -1 || targ.getName().indexOf("\\") > -1)
		{
			pageName = pageName.substring(targ.getName().lastIndexOf("/"));
			if (targ.getName().indexOf("\\") > -1)
				pageName = pageName.substring(targ.getName().lastIndexOf("\\"));
		}
		
		InputStream html = readGetDataAndBuildHtml(is, pageName);

		try
		{
			int n;
			while ((n = html.read(buf)) > 0)
			{
				ps.write(buf, 0, n);
			}
		}
		finally
		{
			is.close();
		}
	}
	
	private InputStream readGetDataAndBuildHtml(InputStream is, String pageName)
	{
		InputStream inStream = null;
		if (pageName.equalsIgnoreCase("index.html") || pageName.equalsIgnoreCase("bsid.html"))
		{
			inStream = buildHomePage(is);
		}
		else
		if (pageName.equalsIgnoreCase("view_connection_log.html"))
		{
			inStream = buildConnectionLog(is);
		}
		else
		if (pageName.equalsIgnoreCase("view_history_log.html"))
		{
			inStream = buildHistoryLog(is);
		}
		else
			inStream = is;
		
		return inStream;
	}
	
	private InputStream buildHomePage(InputStream is)
	{
		InputStream inStream = null;
		
		String html = null;
		try
		{
			BufferedReader bin = new BufferedReader(new InputStreamReader(is));
			String inputLine = null;
			StringBuffer htmlBuffer = new StringBuffer(2000); 
			while ((inputLine = bin.readLine()) != null)
				htmlBuffer.append(inputLine);
			bin.close();
	
			if (htmlBuffer.length() > 0)
			{
				html = htmlBuffer.toString();

				String ipAddress            = getLocalIpAddress();
				String userName             = System.getProperty("user.name");
				String os                   = System.getProperty("os.name");
				String osArch               = System.getProperty("os.arch");
				String osVersion            = System.getProperty("os.version");
				String jreVendor            = System.getProperty("java.vendor");
				String jreVersion           = System.getProperty("java.version");
				String jreInstallDir        = System.getProperty("java.home");
				
				String timeConnected        = "-";
				String manufacturer         = "";
				String version              = "";
				String bsid                 = "";
				String rssi                 = "0";
				String cinr                 = "0";
				String avgRssi              = "0";
				String avgCinr              = "0";
				String maxRssi              = "0";
				String maxCinr              = "0";
				String minRssi              = "0";
				String minCinr              = "0";
				String centerFreq           = "0";
				String totalReceivedBytes   = "0";
				String totalReceivedPackets = "0";
				String prlVersion           = "";
				if (webServer.driver != null)
				{
					timeConnected = "?";
					if (webServer.driver.getList() != null && webServer.driver.getList().length > 0) manufacturer = webServer.driver.getList()[0].getDeviceName();
					if (webServer.driver.getDeviceId() != null) version = webServer.driver.getDeviceId().getApiVersion();
					if (webServer.driver.getLinkStatusInfo() != null)
					{
						bsid       = webServer.driver.getLinkStatusInfo().getBsIdString();
						rssi       = String.valueOf(webServer.driver.getLinkStatusInfo().getRssi());
						cinr       = String.valueOf(webServer.driver.getLinkStatusInfo().getCinr());
						avgRssi    = String.valueOf(webServer.driver.getLinkStatusInfo().getRssiAvg());
						avgCinr    = String.valueOf(webServer.driver.getLinkStatusInfo().getCinrAvg());
						maxRssi    = String.valueOf(webServer.driver.getLinkStatusInfo().getRssiMax());
						maxCinr    = String.valueOf(webServer.driver.getLinkStatusInfo().getCinrMax());
						minRssi    = String.valueOf(webServer.driver.getLinkStatusInfo().getRssiMin());
						minCinr    = String.valueOf(webServer.driver.getLinkStatusInfo().getCinrMin());
						centerFreq = String.valueOf(webServer.driver.getLinkStatusInfo().getCenterFrequency());
					}
					if (webServer.driver.getConnectionStatistics() != null && webServer.driver.getConnectionStatistics().length > 0)
					{
						totalReceivedBytes   = String.valueOf(webServer.driver.getConnectionStatistics()[0].getTotalRxBytes());
						totalReceivedPackets = String.valueOf(webServer.driver.getConnectionStatistics()[0].getTotalTxBytes());
					}
					prlVersion = "";
				}
				
				// Add values into html page
				int start = -1;
				if ((start = html.indexOf("{ip}")) > -1) html = html.substring(0, start)+ipAddress+html.substring(start+11);
				if ((start = html.indexOf("{user_name}")) > -1) html = html.substring(0, start)+userName+html.substring(start+11);
				if ((start = html.indexOf("{os}")) > -1) html = html.substring(0, start)+os+html.substring(start+4);
				if ((start = html.indexOf("{os_architecture}")) > -1) html = html.substring(0, start)+osArch+html.substring(start+17);
				if ((start = html.indexOf("{os_version}")) > -1) html = html.substring(0, start)+osVersion+html.substring(start+12);
				if ((start = html.indexOf("{jre_vendor}")) > -1) html = html.substring(0, start)+jreVendor+html.substring(start+12);
				if ((start = html.indexOf("{jre_version}")) > -1) html = html.substring(0, start)+jreVersion+html.substring(start+13);
				if ((start = html.indexOf("{jre_install_directory}")) > -1) html = html.substring(0, start)+jreInstallDir+html.substring(start+23);
				if ((start = html.indexOf("{time_connected}")) > -1) html = html.substring(0, start)+timeConnected+html.substring(start+16);
				if ((start = html.indexOf("{manufacturer}")) > -1) html = html.substring(0, start)+manufacturer+html.substring(start+14); // Driver Manufacturer
				if ((start = html.indexOf("{version}")) > -1) html = html.substring(0, start)+version+html.substring(start+9); // Driver Version
				if ((start = html.indexOf("{bsid}")) > -1) html = (html.substring(0, start)+bsid+html.substring(start+6));
				if ((start = html.indexOf("{rssi}")) > -1) html = html.substring(0, start)+rssi+html.substring(start+6);
				if ((start = html.indexOf("{cinr}")) > -1) html = html.substring(0, start)+cinr+html.substring(start+6);
				if ((start = html.indexOf("{avg_rssi}")) > -1) html = html.substring(0, start)+avgRssi+html.substring(start+10);
				if ((start = html.indexOf("{avg_cinr}")) > -1) html = html.substring(0, start)+avgCinr+html.substring(start+10);
				if ((start = html.indexOf("{max_rssi}")) > -1) html = html.substring(0, start)+maxRssi+html.substring(start+10);
				if ((start = html.indexOf("{max_cinr}")) > -1) html = html.substring(0, start)+maxCinr+html.substring(start+10);
				if ((start = html.indexOf("{min_rssi}")) > -1) html = html.substring(0, start)+minRssi+html.substring(start+10);
				if ((start = html.indexOf("{min_cinr}")) > -1) html = html.substring(0, start)+minCinr+html.substring(start+10);
				if ((start = html.indexOf("{center_frequency}")) > -1) html = html.substring(0, start)+centerFreq+html.substring(start+18);
				if ((start = html.indexOf("{total_received_bytes}")) > -1) html = html.substring(0, start)+totalReceivedBytes+html.substring(start+22);
				if ((start = html.indexOf("{total_received_packets}")) > -1) html = html.substring(0, start)+totalReceivedPackets+html.substring(start+24);
				if ((start = html.indexOf("{prl_version}")) > -1) html = html.substring(0, start)+prlVersion+html.substring(start+13);
			}
		}
		catch (Exception e)
		{
			XohmLogger.fatalPrintln("Unable to show diagnostic web page: "+e.toString(), null, null);
		}
		
		// Make the html string into an input stream.
		if (html != null)
			inStream = new ByteArrayInputStream(html.getBytes());

		return inStream;
	}
	
	private String getLocalIpAddress()
	{
		String ipAddress = "";
		try
		{
			InetAddress addr = InetAddress.getLocalHost();
			byte[] ipAddr = addr.getAddress();
			 
			// Convert to dot representation
			for (int i=0; i<ipAddr.length; i++)
			{
				if (i > 0)
				{
					ipAddress += ".";
				}
				ipAddress += ipAddr[i]&0xFF;
			}
	   }
		catch (UnknownHostException e)
		{
			XohmLogger.println("Diagnostics: Unable to get loacl IP address: "+e.toString(), null, null);
	   }

		return ipAddress;
	}
	
	private InputStream buildConnectionLog(InputStream is)
	{
		InputStream inStream = null;
		

		return inStream;
	}
	
	private InputStream buildHistoryLog(InputStream is)
	{
		InputStream inStream = null;
		

		return inStream;
	}

	public static void setSuffix(String key, String value)
	{
		map.put(key, value);
	}

	public static void fillMap()
	{
		setSuffix("", "content/unknown");
		setSuffix(".uu", "application/octet-stream");
		setSuffix(".exe", "application/octet-stream");
		setSuffix(".ps", "application/postscript");
		setSuffix(".zip", "application/zip");
		setSuffix(".sh", "application/x-shar");
		setSuffix(".tar", "application/x-tar");
		setSuffix(".snd", "audio/basic");
		setSuffix(".au", "audio/basic");
		setSuffix(".wav", "audio/x-wav");
		setSuffix(".gif", "image/gif");
		setSuffix(".jpg", "image/jpeg");
		setSuffix(".jpeg", "image/jpeg");
		setSuffix(".htm", "text/html");
		setSuffix(".html", "text/html");
		setSuffix(".text", "text/plain");
		setSuffix(".c", "text/plain");
		setSuffix(".cc", "text/plain");
		setSuffix(".c++", "text/plain");
		setSuffix(".h", "text/plain");
		setSuffix(".pl", "text/plain");
		setSuffix(".txt", "text/plain");
		setSuffix(".java", "text/plain");
	}
}
